在 PHP 和 JavaScript 中(或许还有其他语言),变量内所保存的值分为 基本类型值
和 引用类型值
。
$obj = new stdClass;
若一个变量是一个对象,那么该变量保存的就是一个引用类型的值,即变量中实际保存的是堆内存中对象的地址,而不是对象的实体;若变量为其他类型,则保存的是基本类型值,而不是引用地址。这一点需要特别注意,因为我们可能会遇到如下几种情况,不清楚原理可能导致出错。
赋值
$obj_1 = new stdClass;
$obj_2 = $obj_1;
$obj_1->name = 'Xavier';
var_dump($obj_1->name, $obj_2->name);
var_dump($obj_1, $obj_2);
输出:
string(6) "Xavier"
string(6) "Xavier"
object(stdClass)#1 (1) {
["name"]=>
string(6) "Xavier"
}
object(stdClass)#1 (1) {
["name"]=>
string(6) "Xavier"
}
我们发现 obj_2
的 name
也发生的改变,原因是 obj_1
和 obj_2
指向同一个对象 #1
,因为在第二行中,我们将 obj_1
所指向的对象的地址赋给了 obj_2
。
传递参数
我们来看这段代码:
function setName($obj) {
$obj->name = 'Xavier';
}
$person = new stdClass;
setName($person);
var_dump($person->name); // 输出 string(6) "Xavier"
若变量为一个对象,那么当它作为参数传递给一个函数时,同样,传递的是一个对象地址,而不是拷贝了一个新的对象实体给参数 $obj
。这样,函数内部并没有 return 新的东西出来但改变了外部的状态
的这种情况就变得好理解了。
接下来,请看这段代码:
function setName($obj) {
$obj->name = 'Xavier';
$obj = new stdClass;
$obj->name = 'Zhao';
}
$person = new stdClass;
setName($person);
var_dump($person->name); // 输出了 string(6) "Xavier" 而不是 string(6) "Zhao"
最后的输出结果可能会让很多人会疑惑,他们的思维可能是这样的:
- 我将
person
对象的引用地址传递给obj参数
- 在函数内部第一行,根据
obj
的引用地址,我将函数外部对象的name
属性设置成了"Xavier"
- 在第二行,我将一个新的对象赋给了
obj
,既然obj
为函数外部对象的引用,那么外部对象也一定变为了这个新的对象 - 然后我再给这个新的对象设置新的
name
属性"zhao"
,嗯,这样外部对象的name
一定也变成了"zhao"
如果你想的和上方相同,那可就大错特错了,原因在于对传递参数的过程的错误理解。
首先,我们应该明白,将一个变量作为参数传递给函数
可以理解为 将那个变量保存的值 复制一份给 函数的参数(参数即函数范围的局部变量)
。当函数执行时,外部变量
和 函数参数(局部变量)
是同时存在于内存中的,并且两者是相互独立的,虽然两者所保存的值是相同的;函数参数(局部变量)
会在函数执行完毕后被销毁。
明白了上述原理,那么我们重新来看那段代码:
- 函数的
obj
变量在函数内第一行保存的是函数外部person
变量所保存的值,也就是外部对象的地址 - 但到了第二行,
obj
变量所保存的值变成了新创建的对象的地址,obj
的指向发生了改变,而原来的外部对象依旧存在并且只被person
一个变量引用,所以在第三行的行为并没有影响到person
所指向的那个对象。新创建的对象在函数执行之后被销毁。
所以有代码中注释的输出结果。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。